The goal of this script is to generate a Seurat object for sample GSM3717036.

  • removal of cells based on quality control metrics
  • normalization with LogNormalize, for only the remaining cells
  • cell cycle and cell type annotation
  • dimensionality reduction using PCA to obtain 50 dimensions
  • projection using UMAP

We do not perform doublet detection in this dataset.

library(dplyr)
library(patchwork)
library(ggplot2)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

out_dir = "."

We load the parameters :

sample_name = params$sample_name

Input count matrix is there :

count_matrix_dir = paste0(out_dir, "/input/", sample_name, "/")
count_matrix_file = list.files(count_matrix_dir, full.names = TRUE)
count_matrix_file
## [1] "./input/GSM3717036//GSM3717036_DS3-Rie_6_comb_clean.dge.txt.gz"

We load the markers and specific colors for each cell type :

cell_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_cell_markers.rds"))
cell_markers = lapply(cell_markers, FUN = toupper)
lengths(cell_markers)
##          CD4 T cells          CD8 T cells     Langerhans cells 
##                   13                   13                    9 
##          macrophages              B cells              cuticle 
##                   10                   16                   15 
##               cortex              medulla                  IRS 
##                   16                   10                   16 
##        proliferative               HF-SCs            IFE basal 
##                   20                   17                   16 
## IFE granular spinous                  ORS          melanocytes 
##                   17                   15                   10 
##            sebocytes 
##                    8

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We load markers to display on the dotplot :

dotplot_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_dotplot_markers.rds"))
dotplot_markers = lapply(dotplot_markers, FUN = toupper)
dotplot_markers
## $`CD4 T cells`
## [1] "PTPRC" "CD3E"  "CD4"  
## 
## $`CD8 T cells`
## [1] "CD3E" "CD8A"
## 
## $`Langerhans cells`
## [1] "CD207" "CPVL" 
## 
## $macrophages
## [1] "TREM2" "MSR1" 
## 
## $`B cells`
## [1] "CD79A" "CD79B"
## 
## $cuticle
## [1] "MSX2"  "KRT32" "KRT35"
## 
## $cortex
## [1] "KRT31" "PRR9" 
## 
## $medulla
## [1] "BAMBI"   "ADLH1A3"
## 
## $IRS
## [1] "KRT71" "KRT73"
## 
## $proliferative
## [1] "TOP2A" "MCM5"  "TK1"  
## 
## $`HF-SCs`
## [1] "KRT14"  "CXCL14"
## 
## $`IFE basal`
## [1] "COL17A1" "KRT15"  
## 
## $`IFE granular spinous`
## [1] "SPINK5" "KRT1"  
## 
## $ORS
## [1] "KRT16" "KRT6C"
## 
## $melanocytes
## [1] "DCT"   "MLANA"
## 
## $sebocytes
## [1] "CLMP"  "PPARG"

We load metadata for this sample :

sample_info = readRDS(paste0(out_dir, "/../1_metadata/takahashi_sample_info.rds"))
sample_info %>%
  dplyr::filter(project_name == sample_name)
##   project_name sample_type sample_identifier platform gender   location
## 1   GSM3717036          HD    Takahashi_HD_3 Drop-Seq      M hair scalp
##   laboratory       color
## 1  Takahashi yellowgreen

We load the correspondence between gene names and Ensembl ID :

gene_corresp = readRDS(paste0(out_dir, "/../1_metadata/takahashi_gene_corresp.rds"))
head(gene_corresp)
##           gene_id  gene_name
## 1 ENSG00000223972    DDX11L1
## 2 ENSG00000227232     WASH7P
## 3 ENSG00000243485 MIR1302-11
## 4 ENSG00000237613    FAM138A
## 5 ENSG00000268020     OR4G4P
## 6 ENSG00000240361    OR4G11P

These is a parameter for different functions :

cl = aquarius::create_parallel_instance(nthreads = 3L)
cut_log_nCount_RNA = 0.5  # almost no filter
cut_nFeature_RNA = 250    # as in the publication
cut_percent.mt = 20
cut_percent.rb = 50

Load count matrix

In this section, we load the count matrix.

mat = read.table(count_matrix_file,
                 header = TRUE, row.names = 1)

# For the two 10X data, we remove the prefix
rownames(mat) = stringr::str_remove(rownames(mat),
                                    pattern = "hg19_")

mat[c(1:5), c(1:5)]
##          ACCGTTCACTAC CTAATAGTTTGA ATAGCATGCACC AGCCCCATTTAG GCAGTCTACCAT
## A1BG-AS1            0            0            0            0            0
## A2M                 0            0            0            0            0
## AAAS                0            0            0            0            0
## AACS                0            0            0            0            0
## AADAC               0            0            0            0            0

(Time to run : 20.4 s)

In genes metadata, we add the Ensembl ID. The sobj@assays$RNA@meta.features dataframe contains three information :

  • Ensembl_ID : EnsemblID, as stored in the gene_corresp table
  • gene_name : gene_name, as stored in the count matrix file. Duplicated gene names will have the same name.

How many genes are in common between the count matrix and the correspondence table ?

ggvenn::ggvenn(list(count_matrix = rownames(mat),
                    annotation = gene_corresp$gene_name), 
               fill_color = c("#0073C2FF", "#EFC000FF"),
               stroke_size = 0.5, set_name_size = 4) +
  ggplot2::ggtitle(label = "Gene names") +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"))

We keep the intersection :

common_genes = intersect(rownames(mat), gene_corresp$gene_name)

length(common_genes)
## [1] 14170
length(unique(common_genes))
## [1] 14170

We define the same order for rownames in the matrix and gene name in the table

# Subset genes in gene_corresp
gene_corresp = gene_corresp %>%
  dplyr::filter(!duplicated(gene_name)) %>%
  dplyr::filter(gene_name %in% common_genes) %>%
  `rownames<-`(.$gene_name) %>%
  `colnames<-`(c("Ensembl_ID", "gene_name"))
gene_corresp = gene_corresp[common_genes, ]

# Subset genes in the matrix
mat = mat[common_genes, ]

# Genes are in the same order
all.equal(gene_corresp$gene_name, rownames(mat))
## [1] TRUE

We create a Seurat object with the genes for which Ensembl ID are available:

sobj = Seurat::CreateSeuratObject(counts = mat,
                                  project = sample_name,
                                  assay = "RNA")
sobj
## An object of class Seurat 
## 14170 features across 3000 samples within 1 assay 
## Active assay: RNA (14170 features, 0 variable features)

We add the correspondence in the Seurat object :

sobj@assays$RNA@meta.features = gene_corresp
rm(gene_corresp)

head(sobj@assays$RNA@meta.features)
##               Ensembl_ID gene_name
## A1BG-AS1 ENSG00000268895  A1BG-AS1
## A2M      ENSG00000175899       A2M
## AAAS     ENSG00000094914      AAAS
## AACS     ENSG00000081760      AACS
## AADAC    ENSG00000114771     AADAC
## AADACL2  ENSG00000197953   AADACL2

We add the same columns as in metadata :

row_oi = (sample_info$project_name == sample_name)

sobj$project_name = sample_name
sobj$sample_identifier = sample_info[row_oi, "sample_identifier"]
sobj$sample_type = sample_info[row_oi, "sample_type"]
sobj$location = sample_info[row_oi, "location"]
sobj$laboratory = sample_info[row_oi, "laboratory"]

colnames(sobj@meta.data)
## [1] "orig.ident"        "nCount_RNA"        "nFeature_RNA"     
## [4] "project_name"      "sample_identifier" "sample_type"      
## [7] "location"          "laboratory"

Before filtering

Normalization

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize",
                             assay = "RNA")

sobj = Seurat::FindVariableFeatures(sobj,
                                    assay = "RNA",
                                    nfeatures = 3000)
sobj
## An object of class Seurat 
## 14170 features across 3000 samples within 1 assay 
## Active assay: RNA (14170 features, 3000 variable features)

Projection

We generate a UMAP to visualize cells before filtering.

sobj = aquarius::dimensions_reduction(sobj = sobj,
                                      assay = "RNA",
                                      reduction = "pca",
                                      max_dims = 50,
                                      verbose = FALSE)
Seurat::ElbowPlot(sobj, ndims = 50, reduction = "RNA_pca")

We generate a UMAP with 20 principal components :

ndims = 20
sobj = Seurat::RunUMAP(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_umap"))

sobj
## An object of class Seurat 
## 14170 features across 3000 samples within 1 assay 
## Active assay: RNA (14170 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_umap

Cell type

We annotate cells for cell type using Seurat::AddModuleScore function.

sobj = aquarius::cell_annot_custom(sobj,
                                   newname = "cell_type",
                                   markers = cell_markers,
                                   use_negative = TRUE,
                                   add_score = TRUE,
                                   verbose = TRUE)

colnames(sobj@meta.data) = stringr::str_replace_all(string = colnames(sobj@meta.data),
                                                    pattern = " ",
                                                    replacement = "_")

sobj$cell_type = factor(sobj$cell_type, levels = names(cell_markers))

table(sobj$cell_type)
## 
##          CD4 T cells          CD8 T cells     Langerhans cells 
##                   19                  107                   63 
##          macrophages              B cells              cuticle 
##                   83                   92                  210 
##               cortex              medulla                  IRS 
##                  396                  168                  426 
##        proliferative               HF-SCs            IFE basal 
##                  165                  225                  173 
## IFE granular spinous                  ORS          melanocytes 
##                  445                  268                   77 
##            sebocytes 
##                   83

(Time to run : 1.8 s)

To justify cell type annotation, we can make a dotplot :

markers = c("PTPRC", "MSX2", "KRT16",
            unique(unlist(dotplot_markers[levels(sobj$cell_type)])))
markers = markers[markers %in% rownames(sobj)]

aquarius::plot_dotplot(sobj, assay = "RNA",
                       column_name = "cell_type",
                       markers = markers,
                       nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
  ggplot2::theme(legend.position = "right",
                 legend.box = "vertical",
                 legend.direction = "vertical",
                 axis.title = element_blank(),
                 axis.text = element_text(size = 15))

We can make a barplot to see the composition of each dataset, and visualize cell types on the projection.

df_proportion = as.data.frame(prop.table(table(sobj$orig.ident,
                                               sobj$cell_type)))
colnames(df_proportion) = c("orig.ident", "cell_type", "freq")

quantif = table(sobj$orig.ident) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("orig.ident", "nb_cells"))

# Plot
plot_list = list()

plot_list[[2]] = aquarius::plot_barplot(df = df_proportion,
                                        x = "orig.ident",
                                        y = "freq",
                                        fill = "cell_type",
                                        position = ggplot2::position_fill()) +
  ggplot2::scale_fill_manual(name = "Cell type",
                             values = color_markers[levels(df_proportion$cell_type)],
                             breaks = levels(df_proportion$cell_type)) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = orig.ident, y = 1.05, label = nb_cells),
                      label.size = 0)

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cell_type") +
  ggplot2::scale_color_manual(values = unlist(color_markers),
                              breaks = names(color_markers)) +
  ggplot2::labs(title = sample_name,
                subtitle = paste0(ncol(sobj), " cells")) +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1, widths = c(6, 1))

Cell cycle phase

We annotate cells for cell cycle phase using Seurat and cyclone.

cc_columns = aquarius::add_cell_cycle(sobj = sobj,
                                      assay = "RNA",
                                      species_rdx = "hs",
                                      BPPARAM = cl)@meta.data[, c("Seurat.Phase", "Phase")]
## 
##   G1  G2M    S 
## 1216  654  383
sobj$Seurat.Phase = cc_columns$Seurat.Phase
sobj$cyclone.Phase = cc_columns$Phase

table(sobj$Seurat.Phase, sobj$cyclone.Phase)
##            
##              G1 G2M   S
##   G1        797 419 265
##   G2M       157 146  41
##   S         262  89  77
##   Undecided   0   0   0

(Time to run : 110.02 s)

We visualize cell cycle on the projection :

plot_list = list()

plot_list[[2]] = Seurat::DimPlot(sobj, group.by = "Seurat.Phase") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "Seurat.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cyclone.Phase") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "cyclone.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1)

Quality control

In this section, we look at the number of genes expressed by each cell, the number of UMI, the percentage of mitochondrial genes expressed, and the percentage of ribosomal genes expressed.

We compute four quality metrics :

sobj = Seurat::PercentageFeatureSet(sobj, pattern = "^MT", col.name = "percent.mt")
sobj = Seurat::PercentageFeatureSet(sobj, pattern = "^RP[L|S][0-9]*$", col.name = "percent.rb")
sobj$log_nCount_RNA = log(sobj$nCount_RNA)

head(sobj@meta.data)
##              orig.ident nCount_RNA nFeature_RNA project_name sample_identifier
## ACCGTTCACTAC GSM3717036      18096         2931   GSM3717036    Takahashi_HD_3
## CTAATAGTTTGA GSM3717036       9759         2632   GSM3717036    Takahashi_HD_3
## ATAGCATGCACC GSM3717036       8860         2219   GSM3717036    Takahashi_HD_3
## AGCCCCATTTAG GSM3717036       6315         1715   GSM3717036    Takahashi_HD_3
## GCAGTCTACCAT GSM3717036       7219         1799   GSM3717036    Takahashi_HD_3
## ATTCGACCAAGG GSM3717036       6019         1915   GSM3717036    Takahashi_HD_3
##              sample_type   location laboratory score_CD4_T_cells
## ACCGTTCACTAC          HD hair scalp  Takahashi       -0.03173062
## CTAATAGTTTGA          HD hair scalp  Takahashi       -0.03901287
## ATAGCATGCACC          HD hair scalp  Takahashi       -0.02660790
## AGCCCCATTTAG          HD hair scalp  Takahashi       -0.01639574
## GCAGTCTACCAT          HD hair scalp  Takahashi       -0.02301986
## ATTCGACCAAGG          HD hair scalp  Takahashi       -0.03194420
##              score_CD8_T_cells score_Langerhans_cells score_macrophages
## ACCGTTCACTAC       -0.02517956            -0.03706621       -0.02696882
## CTAATAGTTTGA       -0.03130051            -0.06178143       -0.04516957
## ATAGCATGCACC       -0.01539585            -0.03819082       -0.02688366
## AGCCCCATTTAG       -0.01658928            -0.03313708       -0.01766800
## GCAGTCTACCAT       -0.02016997            -0.04386187       -0.02501728
## ATTCGACCAAGG       -0.02564684             0.17986723        0.06888658
##              score_B_cells score_cuticle score_cortex score_medulla  score_IRS
## ACCGTTCACTAC   -0.04885937    -0.1568707   0.04728849   -0.12458519 -0.2172633
## CTAATAGTTTGA   -0.04369022    -0.4161303  -0.22339344    0.03208894 -0.4146297
## ATAGCATGCACC   -0.01904630     0.4169276  -0.04715712    0.09942023  0.4438775
## AGCCCCATTTAG   -0.02374051    -0.3587425  -0.14403075   -0.22962979 -0.4963482
## GCAGTCTACCAT   -0.01448828    -0.3065465  -0.34131616   -0.12198573 -0.1098497
## ATTCGACCAAGG   -0.02445677    -0.3577165  -0.30537207   -0.02551720 -0.1824975
##              score_proliferative score_HF-SCs score_IFE_basal
## ACCGTTCACTAC         -0.10280283   -0.1512609      -0.1439294
## CTAATAGTTTGA         -0.11784224   -0.1324589      -0.4527119
## ATAGCATGCACC         -0.07493882   -0.2269830      -0.4211259
## AGCCCCATTTAG         -0.09823062   -0.3938491      -0.2336014
## GCAGTCTACCAT         -0.10514026   -0.2656295      -0.1401280
## ATTCGACCAAGG         -0.15432357   -0.0125482      -0.2495967
##              score_IFE_granular_spinous  score_ORS score_melanocytes
## ACCGTTCACTAC                -0.24809709 -0.3564199       -0.11506010
## CTAATAGTTTGA                -0.33222371  0.3465121        0.05130075
## ATAGCATGCACC                -0.56692919 -0.2945117       -0.34625640
## AGCCCCATTTAG                 0.97725701  0.0233339       -0.21412652
## GCAGTCTACCAT                 0.86454853 -0.1219940       -0.33200143
## ATTCGACCAAGG                 0.02576955  0.6104269       -0.07127572
##              score_sebocytes            cell_type Seurat.Phase cyclone.Phase
## ACCGTTCACTAC     -0.14717899               cortex           G1            G1
## CTAATAGTTTGA      0.11171118                  ORS           G1            G1
## ATAGCATGCACC     -0.05843992              cuticle            S            G1
## AGCCCCATTTAG     -0.18315066 IFE granular spinous           G1            G1
## GCAGTCTACCAT     -0.01954408 IFE granular spinous           G1            G1
## ATTCGACCAAGG      0.04424119                  ORS           G1            G1
##              percent.mt percent.rb log_nCount_RNA
## ACCGTTCACTAC   2.193855   7.222591       9.803446
## CTAATAGTTTGA   1.977662  14.284250       9.185945
## ATAGCATGCACC   2.155756  23.735892       9.089302
## AGCCCCATTTAG   1.662708  11.068884       8.750683
## GCAGTCTACCAT   3.227594  17.066076       8.884472
## ATTCGACCAAGG   1.063300  10.898820       8.702676

We get the cell barcodes for the failing cells :

fail_percent.mt = sobj@meta.data %>% dplyr::filter(percent.mt > cut_percent.mt) %>% rownames()
fail_percent.rb = sobj@meta.data %>% dplyr::filter(percent.rb > cut_percent.rb) %>% rownames()
fail_log_nCount_RNA = sobj@meta.data %>% dplyr::filter(log_nCount_RNA < cut_log_nCount_RNA) %>% rownames()
fail_nFeature_RNA = sobj@meta.data %>% dplyr::filter(nFeature_RNA < cut_nFeature_RNA) %>% rownames()

Quality control representation

We can visualize the 4 cells quality with a Venn diagram :

n_filtered = c(fail_percent.mt, fail_percent.rb, fail_log_nCount_RNA, fail_nFeature_RNA) %>%
  unique() %>% length()
percent_filtered = round(100*(n_filtered/ncol(sobj)), 2)

ggvenn::ggvenn(list(percent.mt = fail_percent.mt,
                    percent.rb = fail_percent.rb,
                    log_nCount_RNA = fail_log_nCount_RNA,
                    nFeature_RNA = fail_nFeature_RNA), 
               fill_color = c("#0073C2FF", "#EFC000FF", "orange", "pink"),
               stroke_size = 0.5, set_name_size = 4) +
  ggplot2::labs(title = "Filtered out cells",
                subtitle = paste0(n_filtered, " cells (", percent_filtered, " % of all cells)")) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5))

Number of UMI

To visualize the threshold for number of UMI, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "log_nCount_RNA",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_log_nCount_RNA)

Seurat::VlnPlot(sobj, features = "log_nCount_RNA", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_log_nCount_RNA, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_log_nCount_RNA,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "log_nCount_RNA",
                subtitle = paste0(length(fail_log_nCount_RNA), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Number of features

To visualize the threshold for number of features, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "nFeature_RNA",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_nFeature_RNA)

Seurat::VlnPlot(sobj, features = "nFeature_RNA", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_nFeature_RNA, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_nFeature_RNA,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "nFeature_RNA",
                subtitle = paste0(length(fail_nFeature_RNA), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Mitochondrial genes expression

To identify a threshold for mitochondrial gene expression, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "percent.mt",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_percent.mt)

Seurat::VlnPlot(sobj, features = "percent.mt", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_percent.mt, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_percent.mt,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "percent.mt",
                subtitle = paste0(length(fail_percent.mt), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Ribosomal genes expression

To identify a threshold for ribosomal gene expression, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "percent.rb",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_percent.rb)

Seurat::VlnPlot(sobj, features = "percent.rb", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_percent.rb, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_percent.rb,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "percent.rb",
                subtitle = paste0(length(fail_percent.rb), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

FACS-like figure

We would like to see if the number of feature expressed by cell, and the number of UMI is correlated with the cell type, the percentage of mitochondrial and ribosomal gene expressed. We build the log_nCount_RNA by nFeature_RNA figure, where cells (dots) are colored by these different metrics.

This is the figure, colored by cell type :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "cell_type",
                           col_colors = unname(color_markers),
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the percentage of mitochondrial genes expressed in cell :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "percent.mt",
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the percentage of ribosomal genes expressed in cell :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "percent.rb",
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

Visualization as piechart

Do filtered cells belong to a particular cell type ?

sobj$all_cells = TRUE

plot_list = list()

## All cells
df = sobj@meta.data
if (nrow(df) == 0) {
  plot_list[[1]] = ggplot()
} else {
  plot_list[[1]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = "All cells",
                  subtitle = paste(nrow(df), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## Doublet cells: not done
plot_list[[2]] = ggplot()

## percent.mt
df = sobj@meta.data %>%
  dplyr::filter(percent.mt > cut_percent.mt)
if (nrow(df) == 0) {
  plot_list[[3]] = ggplot()
} else {
  plot_list[[3]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("percent.mt >", cut_percent.mt),
                  subtitle = paste(length(fail_percent.mt), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## percent.rb
df = sobj@meta.data %>%
  dplyr::filter(percent.rb > cut_percent.rb)
if (nrow(df) == 0) {
  plot_list[[4]] = ggplot()
} else {
  plot_list[[4]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("percent.rb >", cut_percent.rb),
                  subtitle = paste(length(fail_percent.rb), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## log_nCount_RNA
df = sobj@meta.data %>%
  dplyr::filter(log_nCount_RNA < cut_log_nCount_RNA)
if (nrow(df) == 0) {
  plot_list[[5]] = ggplot()
} else {
  plot_list[[5]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("log_nCount_RNA <", round(cut_log_nCount_RNA, 2)),
                  subtitle = paste(length(fail_log_nCount_RNA), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## nFeature_RNA
df = sobj@meta.data %>%
  dplyr::filter(nFeature_RNA < cut_nFeature_RNA)
if (nrow(df) == 0) {
  plot_list[[6]] = ggplot()
} else {
  plot_list[[6]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("nFeature_RNA <", round(cut_nFeature_RNA, 2)),
                  subtitle = paste(length(fail_nFeature_RNA), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

patchwork::wrap_plots(plot_list, ncol = 3) +
  patchwork::plot_layout(guides = "collect") &
  ggplot2::theme(legend.position = "right")

Save

We could save this object before filtering (remove eval = FALSE) :

saveRDS(sobj, paste0(out_dir, "/datasets/", sample_name, "_sobj_unfiltered.rds"))

Filtering

We remove :

  • cells with a number of UMI lower than 0.5
  • cells expressing a number of genes lower than 250
  • cells having more than 20 % of UMI related to mitochondrial genes
  • cells having more than 50 % of UMI related to ribosomal genes

Note: We do not filter cells detected as doublets. Indeed, few genes and transcripts are detected per cell, and the best cells are therefore annotated as doublets.

sobj = subset(sobj, invert = TRUE,
              cells = unique(c(fail_log_nCount_RNA, fail_nFeature_RNA,
                               fail_percent.mt, fail_percent.rb)))
sobj
## An object of class Seurat 
## 14170 features across 510 samples within 1 assay 
## Active assay: RNA (14170 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_umap

Post-filtering processing

Normalization

We normalize the count matrix for remaining cells :

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize",
                             assay = "RNA")

sobj = Seurat::FindVariableFeatures(sobj,
                                    assay = "RNA",
                                    nfeatures = 3000)
sobj
## An object of class Seurat 
## 14170 features across 510 samples within 1 assay 
## Active assay: RNA (14170 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_umap

Projection

We perform a PCA :

sobj = aquarius::dimensions_reduction(sobj = sobj,
                                      assay = "RNA",
                                      reduction = "pca",
                                      max_dims = 50,
                                      verbose = FALSE)
Seurat::ElbowPlot(sobj, ndims = 50, reduction = "RNA_pca")

We generate a UMAP with 20 principal components :

ndims = 20
sobj = Seurat::RunUMAP(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_umap"))

Annotation

We annotate cells for cell type, with the new normalized expression matrix :

score_columns = grep(x = colnames(sobj@meta.data), pattern = "^score", value = TRUE)
sobj@meta.data[, score_columns] = NULL
sobj$cell_type = NULL

sobj = aquarius::cell_annot_custom(sobj,
                                   newname = "cell_type",
                                   markers = cell_markers,
                                   use_negative = TRUE,
                                   add_score = TRUE,
                                   verbose = TRUE)

sobj$cell_type = factor(sobj$cell_type, levels = names(cell_markers))

colnames(sobj@meta.data) = stringr::str_replace_all(string = colnames(sobj@meta.data),
                                                    pattern = " ",
                                                    replacement = "_")

table(sobj$cell_type)
## 
##          CD4 T cells          CD8 T cells     Langerhans cells 
##                    2                    2                    3 
##          macrophages              B cells              cuticle 
##                    3                    9                   46 
##               cortex              medulla                  IRS 
##                   35                   15                   40 
##        proliferative               HF-SCs            IFE basal 
##                   26                   65                   42 
## IFE granular spinous                  ORS          melanocytes 
##                  123                   67                   13 
##            sebocytes 
##                   19

(Time to run : 1.69 s)

To justify cell type annotation, we can make a dotplot :

markers = c("PTPRC", unique(unlist(dotplot_markers[levels(sobj$cell_type)])))
markers = markers[markers %in% rownames(sobj)]

aquarius::plot_dotplot(sobj, assay = "RNA",
                       column_name = "cell_type",
                       markers = markers,
                       nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
  ggplot2::theme(legend.position = "right",
                 legend.box = "vertical",
                 legend.direction = "vertical",
                 axis.title = element_blank(),
                 axis.text = element_text(size = 15))

We can make a barplot to see the composition of each dataset, and visualize cell types on the projection.

df_proportion = as.data.frame(prop.table(table(sobj$orig.ident,
                                               sobj$cell_type)))
colnames(df_proportion) = c("orig.ident", "cell_type", "freq")

quantif = table(sobj$orig.ident) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("orig.ident", "nb_cells"))

# Plot
plot_list = list()

plot_list[[2]] = aquarius::plot_barplot(df = df_proportion,
                                        x = "orig.ident",
                                        y = "freq",
                                        fill = "cell_type",
                                        position = ggplot2::position_fill()) +
  ggplot2::scale_fill_manual(name = "Cell type",
                             values = color_markers[levels(df_proportion$cell_type)],
                             breaks = levels(df_proportion$cell_type)) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = orig.ident, y = 1.05, label = nb_cells),
                      label.size = 0)

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cell_type",
                                 reduction = "RNA_pca_20_umap") +
  ggplot2::scale_color_manual(values = unlist(color_markers),
                              breaks = names(color_markers)) +
  ggplot2::labs(title = sample_name,
                subtitle = paste0(ncol(sobj), " cells")) +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1, widths = c(6, 1))

Cell cycle

We annotate cells for cell cycle phase :

cc_columns = aquarius::add_cell_cycle(sobj = sobj,
                                      assay = "RNA",
                                      species_rdx = "hs",
                                      BPPARAM = cl)@meta.data[, c("Seurat.Phase", "Phase")]
## 
##  G1 G2M   S 
## 402  53  55
sobj$Seurat.Phase = cc_columns$Seurat.Phase
sobj$cyclone.Phase = cc_columns$Phase

table(sobj$Seurat.Phase, sobj$cyclone.Phase)
##      
##        G1 G2M   S
##   G1  187  25  29
##   G2M 106  18  14
##   S   109  10  12

(Time to run : 29.27 s)

We visualize cell cycle on the projection :

plot_list = list()

plot_list[[2]] = Seurat::DimPlot(sobj, group.by = "Seurat.Phase",
                                 reduction = "RNA_pca_20_umap") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "Seurat.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cyclone.Phase",
                                 reduction = "RNA_pca_20_umap") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "cyclone.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1)

Clustering

We make a highly resolutive clustering :

sobj = Seurat::FindNeighbors(sobj, reduction = "RNA_pca", dims = c(1:ndims))
sobj = Seurat::FindClusters(sobj, resolution = 2)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 510
## Number of edges: 17622
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.4912
## Number of communities: 10
## Elapsed time: 0 seconds
table(sobj$seurat_clusters)
## 
##  0  1  2  3  4  5  6  7  8  9 
## 83 70 69 55 53 46 45 44 25 20

Visualization

Cell type

We can visualize the cell type :

Seurat::DimPlot(sobj, group.by = "cell_type",
                reduction = paste0("RNA_pca_", ndims, "_umap"), cols = color_markers) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

Cell cycle

We can visualize the cell cycle, from Seurat :

Seurat::DimPlot(sobj, group.by = "Seurat.Phase",
                reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

We can visualize the cell cycle, from cyclone :

Seurat::DimPlot(sobj, group.by = "cyclone.Phase",
                reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

Clusters

We visualize the clustering :

Seurat::DimPlot(sobj, group.by = "seurat_clusters", label = TRUE,
                reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

Gene expression

We visualize all cell types markers on the UMAP :

markers = dotplot_markers %>% unlist() %>% unname()
markers = markers[markers %in% rownames(sobj)]

plot_list = lapply(markers,
                   FUN = function(one_gene) {
                     p = Seurat::FeaturePlot(sobj, features = one_gene,
                                             reduction = paste0("RNA_pca_", ndims, "_umap")) +
                       ggplot2::labs(title = one_gene) +
                       ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
                       ggplot2::theme(aspect.ratio = 1,
                                      plot.subtitle = element_text(hjust = 0.5)) +
                       Seurat::NoAxes()
                     return(p)
                   })

patchwork::wrap_plots(plot_list, ncol = 4)

Save

We save the annotated and filtered Seurat object :

saveRDS(sobj, file = paste0(out_dir, "/datasets/", sample_name, "_sobj_filtered.rds"))

R session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_3.3.5   patchwork_1.1.2 dplyr_1.0.7    
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [37] Rcpp_1.0.9                  readr_2.0.2                
##  [39] KernSmooth_2.23-17          carrier_0.1.0              
##  [41] promises_1.1.0              gdata_2.18.0               
##  [43] DelayedArray_0.12.3         limma_3.42.2               
##  [45] graph_1.64.0                RcppParallel_5.1.4         
##  [47] Hmisc_4.4-0                 fs_1.5.2                   
##  [49] RSpectra_0.16-0             fastmatch_1.1-0            
##  [51] ranger_0.12.1               digest_0.6.25              
##  [53] png_0.1-7                   sctransform_0.2.1          
##  [55] cowplot_1.0.0               DOSE_3.12.0                
##  [57] ggvenn_0.1.9                here_1.0.1                 
##  [59] TInGa_0.0.0.9000            ggraph_2.0.3               
##  [61] pkgconfig_2.0.3             GO.db_3.10.0               
##  [63] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [65] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [67] DropletUtils_1.6.1          reticulate_1.26            
##  [69] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [71] circlize_0.4.15             beeswarm_0.4.0             
##  [73] GetoptLong_1.0.5            xfun_0.35                  
##  [75] bslib_0.3.1                 zoo_1.8-10                 
##  [77] tidyselect_1.1.0            reshape2_1.4.4             
##  [79] purrr_0.3.4                 ica_1.0-2                  
##  [81] pcaPP_1.9-73                viridisLite_0.3.0          
##  [83] rtracklayer_1.46.0          rlang_1.0.2                
##  [85] hexbin_1.28.1               jquerylib_0.1.4            
##  [87] dyneval_0.9.9               glue_1.4.2                 
##  [89] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [91] stringr_1.4.0               lava_1.6.7                 
##  [93] europepmc_0.3               DESeq2_1.26.0              
##  [95] recipes_0.1.17              labeling_0.3               
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            Biobase_2.46.0             
## [147] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [149] lmtest_0.9-38               msigdbr_7.5.1              
## [151] htmlTable_1.13.3            triebeard_0.3.0            
## [153] lsei_1.2-0                  xtable_1.8-4               
## [155] ROCR_1.0-7                  BiocManager_1.30.10        
## [157] scatterplot3d_0.3-41        abind_1.4-5                
## [159] farver_2.0.3                parallelly_1.28.1          
## [161] RANN_2.6.1                  askpass_1.1                
## [163] GenomicRanges_1.38.0        RcppAnnoy_0.0.16           
## [165] tibble_3.1.5                ggdendro_0.1-20            
## [167] cluster_2.1.0               future.apply_1.5.0         
## [169] Seurat_3.1.5                dendextend_1.15.1          
## [171] Matrix_1.3-2                ellipsis_0.3.2             
## [173] prettyunits_1.1.1           lubridate_1.7.9            
## [175] ggridges_0.5.2              igraph_1.2.5               
## [177] RcppEigen_0.3.3.7.0         fgsea_1.12.0               
## [179] remotes_2.4.2               scBFA_1.0.0                
## [181] destiny_3.0.1               VIM_6.1.1                  
## [183] testthat_3.1.0              htmltools_0.5.2            
## [185] BiocFileCache_1.10.2        yaml_2.2.1                 
## [187] utf8_1.1.4                  plotly_4.9.2.1             
## [189] XML_3.99-0.3                ModelMetrics_1.2.2.2       
## [191] e1071_1.7-3                 foreign_0.8-76             
## [193] withr_2.5.0                 fitdistrplus_1.0-14        
## [195] BiocParallel_1.20.1         xgboost_1.4.1.1            
## [197] bit64_4.0.5                 foreach_1.5.0              
## [199] robustbase_0.93-9           Biostrings_2.54.0          
## [201] GOSemSim_2.13.1             rsvd_1.0.3                 
## [203] memoise_2.0.0               evaluate_0.18              
## [205] forcats_0.5.0               rio_0.5.16                 
## [207] geneplotter_1.64.0          tzdb_0.1.2                 
## [209] caret_6.0-86                ps_1.6.0                   
## [211] DiagrammeR_1.0.6.1          curl_4.3                   
## [213] fdrtool_1.2.15              fansi_0.4.1                
## [215] highr_0.8                   urltools_1.7.3             
## [217] xts_0.12.1                  GSEABase_1.48.0            
## [219] acepack_1.4.1               edgeR_3.28.1               
## [221] checkmate_2.0.0             scds_1.2.0                 
## [223] cachem_1.0.6                npsurv_0.4-0               
## [225] babelgene_22.3              rjson_0.2.20               
## [227] openxlsx_4.1.5              ggrepel_0.9.1              
## [229] clue_0.3-60                 rprojroot_2.0.2            
## [231] stabledist_0.7-1            tools_3.6.3                
## [233] sass_0.4.0                  nichenetr_1.1.1            
## [235] magrittr_2.0.1              RCurl_1.98-1.2             
## [237] proxy_0.4-24                car_3.0-11                 
## [239] ape_5.3                     ggplotify_0.0.5            
## [241] xml2_1.3.2                  httr_1.4.2                 
## [243] assertthat_0.2.1            rmarkdown_2.18             
## [245] boot_1.3-25                 globals_0.14.0             
## [247] R6_2.4.1                    Rhdf5lib_1.8.0             
## [249] nnet_7.3-14                 RcppHNSW_0.2.0             
## [251] progress_1.2.2              genefilter_1.68.0          
## [253] statmod_1.4.34              gtools_3.8.2               
## [255] shape_1.4.6                 HDF5Array_1.14.4           
## [257] BiocSingular_1.2.2          rhdf5_2.30.1               
## [259] splines_3.6.3               AUCell_1.8.0               
## [261] carData_3.0-4               colorspace_1.4-1           
## [263] generics_0.1.0              stats4_3.6.3               
## [265] base64enc_0.1-3             dynfeature_1.0.0           
## [267] smoother_1.1                gridtext_0.1.1             
## [269] pillar_1.6.3                tweenr_1.0.1               
## [271] sp_1.4-1                    ggplot.multistats_1.0.0    
## [273] rvcheck_0.1.8               GenomeInfoDbData_1.2.2     
## [275] plyr_1.8.6                  gtable_0.3.0               
## [277] zip_2.2.0                   knitr_1.41                 
## [279] ComplexHeatmap_2.14.0       latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             grid_3.6.3                 
## [303] lazyeval_0.2.2              Formula_1.2-3              
## [305] tsne_0.1-3                  crayon_1.3.4               
## [307] MASS_7.3-54                 pROC_1.16.2                
## [309] viridis_0.5.1               dynparam_1.0.0             
## [311] rpart_4.1-15                zinbwave_1.8.0             
## [313] compiler_3.6.3              ggtext_0.1.0
LS0tCnBhcmFtczoKICBzYW1wbGVfbmFtZTogIkdTTTM3MTcwMzgiCnRpdGxlOiAiR1NFMTI5NjExIGRhdGFzZXQiCnN1YnRpdGxlOiAiU2FtcGxlIGByIHBhcmFtcyRzYW1wbGVfbmFtZWAiCmF1dGhvcjogIkF1ZHJleSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQnKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQotLS0KCjxzdHlsZT4KYm9keSB7CnRleHQtYWxpZ246IGp1c3RpZnl9Cjwvc3R5bGU+Cgo8IS0tIEF1dG9tYXRpY2FsbHkgY29tcHV0ZXMgYW5kIHByaW50cyBpbiB0aGUgb3V0cHV0IHRoZSBydW5uaW5nIHRpbWUgZm9yIGFueSBjb2RlIGNodW5rIC0tPgpgYGB7ciwgZWNobz1GQUxTRX0KIyBodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9ybWFya2Rvd24vaXNzdWVzLzE0NTMKaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGRfIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PiIsICJzaG93IiwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpLAogIHRpbWVfaXQgPSBsb2NhbCh7CiAgICBub3cgPSBOVUxMCiAgICBmdW5jdGlvbihiZWZvcmUsIG9wdGlvbnMpIHsKICAgICAgaWYgKG9wdGlvbnMkdGltZV9pdCkgewogICAgICAgIGlmIChiZWZvcmUpIHsKICAgICAgICAgIG5vdyA8PC0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsbCBmb3Igc2Vzc2lvbkluZm8oKQogICAgICAgICAgICAgICAgICAgICAgZm9sZF9wbG90ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgZmlndXJlIHNldHRpbmdzCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAnY2VudGVyJywKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDIwLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDE1LAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIHNvbWV0aGluZyBhYm91dCBzZWVkLCBjaHVuayBhbmQgUm1hcmtkb3duIGNvbXBpbGF0aW9uCiAgICAgICAgICAgICAgICAgICAgICAjIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzM5NDE3MDAzL2xvbmctdmVjdG9ycy1ub3Qtc3VwcG9ydGVkLXlldC1lcnJvci1pbi1ybWQtYnV0LW5vdC1pbi1yLXNjcmlwdAogICAgICAgICAgICAgICAgICAgICAgIyBjYWNoZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBjYWNoZS5sYXp5ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIGFkZCBydW50aW1lIGFmdGVyIGNodW5rCiAgICAgICAgICAgICAgICAgICAgICB0aW1lX2l0ID0gRkFMU0UpCmBgYAoKClRoZSBnb2FsIG9mIHRoaXMgc2NyaXB0IGlzIHRvIGdlbmVyYXRlIGEgU2V1cmF0IG9iamVjdCBmb3Igc2FtcGxlIGByIHBhcmFtcyRzYW1wbGVfbmFtZWAuCgoqIHJlbW92YWwgb2YgY2VsbHMgYmFzZWQgb24gcXVhbGl0eSBjb250cm9sIG1ldHJpY3MKKiBub3JtYWxpemF0aW9uIHdpdGggYExvZ05vcm1hbGl6ZWAsIGZvciBvbmx5IHRoZSByZW1haW5pbmcgY2VsbHMKKiBjZWxsIGN5Y2xlIGFuZCBjZWxsIHR5cGUgYW5ub3RhdGlvbgoqIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiB1c2luZyBgUENBYCB0byBvYnRhaW4gNTAgZGltZW5zaW9ucwoqIHByb2plY3Rpb24gdXNpbmcgYFVNQVBgCgpXZSBkbyBub3QgcGVyZm9ybSBkb3VibGV0IGRldGVjdGlvbiBpbiB0aGlzIGRhdGFzZXQuCgoKYGBge3IgbGlicmFyeX0KbGlicmFyeShkcGx5cikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2dwbG90MikKCi5saWJQYXRocygpCmBgYAoKIyBQcmVwYXJhdGlvbgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBzZXQgdGhlIGdsb2JhbCBzZXR0aW5ncyBvZiB0aGUgYW5hbHlzaXMuIFdlIHdpbGwgc3RvcmUgZGF0YSB0aGVyZSA6CgpgYGB7ciBvdXRfZGlyfQpvdXRfZGlyID0gIi4iCmBgYAoKV2UgbG9hZCB0aGUgcGFyYW1ldGVycyA6CgpgYGB7ciBnZXRfcGFyYW19CnNhbXBsZV9uYW1lID0gcGFyYW1zJHNhbXBsZV9uYW1lCmBgYAoKSW5wdXQgY291bnQgbWF0cml4IGlzIHRoZXJlIDoKCmBgYHtyIGNvdW50X21hdHJpeF9kaXJ9CmNvdW50X21hdHJpeF9kaXIgPSBwYXN0ZTAob3V0X2RpciwgIi9pbnB1dC8iLCBzYW1wbGVfbmFtZSwgIi8iKQpjb3VudF9tYXRyaXhfZmlsZSA9IGxpc3QuZmlsZXMoY291bnRfbWF0cml4X2RpciwgZnVsbC5uYW1lcyA9IFRSVUUpCmNvdW50X21hdHJpeF9maWxlCmBgYAoKCldlIGxvYWQgdGhlIG1hcmtlcnMgYW5kIHNwZWNpZmljIGNvbG9ycyBmb3IgZWFjaCBjZWxsIHR5cGUgOgoKYGBge3IgY2VsbF9tYXJrZXJzfQpjZWxsX21hcmtlcnMgPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLy4uLy4uLzFfbWV0YWRhdGEvaHNfaGRfY2VsbF9tYXJrZXJzLnJkcyIpKQpjZWxsX21hcmtlcnMgPSBsYXBwbHkoY2VsbF9tYXJrZXJzLCBGVU4gPSB0b3VwcGVyKQpsZW5ndGhzKGNlbGxfbWFya2VycykKYGBgCgpIZXJlIGFyZSBjdXN0b20gY29sb3JzIGZvciBlYWNoIGNlbGwgdHlwZSA6CgpgYGB7ciBjb2xvcl9tYXJrZXJzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8uLi8xX21ldGFkYXRhL2hzX2hkX2NvbG9yX21hcmtlcnMucmRzIikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgIGNvbG9yID0gdW5saXN0KGNvbG9yX21hcmtlcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKCldlIGxvYWQgbWFya2VycyB0byBkaXNwbGF5IG9uIHRoZSBkb3RwbG90IDoKCmBgYHtyIGRvdHBsb3RfbWFya2Vyc30KZG90cGxvdF9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8uLi8xX21ldGFkYXRhL2hzX2hkX2RvdHBsb3RfbWFya2Vycy5yZHMiKSkKZG90cGxvdF9tYXJrZXJzID0gbGFwcGx5KGRvdHBsb3RfbWFya2VycywgRlVOID0gdG91cHBlcikKZG90cGxvdF9tYXJrZXJzCmBgYAoKCldlIGxvYWQgbWV0YWRhdGEgZm9yIHRoaXMgc2FtcGxlIDoKCmBgYHtyIHNhbXBsZV9pbmZvfQpzYW1wbGVfaW5mbyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vMV9tZXRhZGF0YS90YWthaGFzaGlfc2FtcGxlX2luZm8ucmRzIikpCnNhbXBsZV9pbmZvICU+JQogIGRwbHlyOjpmaWx0ZXIocHJvamVjdF9uYW1lID09IHNhbXBsZV9uYW1lKQpgYGAKCldlIGxvYWQgdGhlIGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gZ2VuZSBuYW1lcyBhbmQgRW5zZW1ibCBJRCA6CgpgYGB7ciBnZW5lX2NvcnJlc3B9CmdlbmVfY29ycmVzcCA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vMV9tZXRhZGF0YS90YWthaGFzaGlfZ2VuZV9jb3JyZXNwLnJkcyIpKQpoZWFkKGdlbmVfY29ycmVzcCkKYGBgCgpUaGVzZSBpcyBhIHBhcmFtZXRlciBmb3IgZGlmZmVyZW50IGZ1bmN0aW9ucyA6CgpgYGB7ciBnbG9iYWxfc2V0dGluZ3N9CmNsID0gYXF1YXJpdXM6OmNyZWF0ZV9wYXJhbGxlbF9pbnN0YW5jZShudGhyZWFkcyA9IDNMKQpjdXRfbG9nX25Db3VudF9STkEgPSAwLjUgICMgYWxtb3N0IG5vIGZpbHRlcgpjdXRfbkZlYXR1cmVfUk5BID0gMjUwICAgICMgYXMgaW4gdGhlIHB1YmxpY2F0aW9uCmN1dF9wZXJjZW50Lm10ID0gMjAKY3V0X3BlcmNlbnQucmIgPSA1MApgYGAKCiMgTG9hZCBjb3VudCBtYXRyaXgKCkluIHRoaXMgc2VjdGlvbiwgd2UgbG9hZCB0aGUgY291bnQgbWF0cml4LgoKYGBge3IgbG9hZF9jb3VudF9tYXRyaXgsIHRpbWVfaXQgPSBUUlVFfQptYXQgPSByZWFkLnRhYmxlKGNvdW50X21hdHJpeF9maWxlLAogICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCgojIEZvciB0aGUgdHdvIDEwWCBkYXRhLCB3ZSByZW1vdmUgdGhlIHByZWZpeApyb3duYW1lcyhtYXQpID0gc3RyaW5ncjo6c3RyX3JlbW92ZShyb3duYW1lcyhtYXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gImhnMTlfIikKCm1hdFtjKDE6NSksIGMoMTo1KV0KYGBgCgpJbiBnZW5lcyBtZXRhZGF0YSwgd2UgYWRkIHRoZSBFbnNlbWJsIElELiBUaGUgYHNvYmpAYXNzYXlzJFJOQUBtZXRhLmZlYXR1cmVzYCBkYXRhZnJhbWUgY29udGFpbnMgdGhyZWUgaW5mb3JtYXRpb24gOgoKKiBgRW5zZW1ibF9JRGAgOiBFbnNlbWJsSUQsIGFzIHN0b3JlZCBpbiB0aGUgYGdlbmVfY29ycmVzcGAgdGFibGUKKiBgZ2VuZV9uYW1lYCA6IGdlbmVfbmFtZSwgYXMgc3RvcmVkIGluIHRoZSBjb3VudCBtYXRyaXggZmlsZS4gRHVwbGljYXRlZCBnZW5lIG5hbWVzIHdpbGwgaGF2ZSB0aGUgc2FtZSBuYW1lLgoKSG93IG1hbnkgZ2VuZXMgYXJlIGluIGNvbW1vbiBiZXR3ZWVuIHRoZSBjb3VudCBtYXRyaXggYW5kIHRoZSBjb3JyZXNwb25kZW5jZSB0YWJsZSA/CgpgYGB7ciB2ZW5uX2dlbmVfbmFtZXMsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNn0KZ2d2ZW5uOjpnZ3Zlbm4obGlzdChjb3VudF9tYXRyaXggPSByb3duYW1lcyhtYXQpLAogICAgICAgICAgICAgICAgICAgIGFubm90YXRpb24gPSBnZW5lX2NvcnJlc3AkZ2VuZV9uYW1lKSwgCiAgICAgICAgICAgICAgIGZpbGxfY29sb3IgPSBjKCIjMDA3M0MyRkYiLCAiI0VGQzAwMEZGIiksCiAgICAgICAgICAgICAgIHN0cm9rZV9zaXplID0gMC41LCBzZXRfbmFtZV9zaXplID0gNCkgKwogIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSAiR2VuZSBuYW1lcyIpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkKYGBgCgpXZSBrZWVwIHRoZSBpbnRlcnNlY3Rpb24gOgoKYGBge3IgY29tbW9uX2dlbmVzfQpjb21tb25fZ2VuZXMgPSBpbnRlcnNlY3Qocm93bmFtZXMobWF0KSwgZ2VuZV9jb3JyZXNwJGdlbmVfbmFtZSkKCmxlbmd0aChjb21tb25fZ2VuZXMpCmxlbmd0aCh1bmlxdWUoY29tbW9uX2dlbmVzKSkKYGBgCgpXZSBkZWZpbmUgdGhlIHNhbWUgb3JkZXIgZm9yIHJvd25hbWVzIGluIHRoZSBtYXRyaXggYW5kIGdlbmUgbmFtZSBpbiB0aGUgdGFibGUKCmBgYHtyIGZlYXR1cmVzX2RmfQojIFN1YnNldCBnZW5lcyBpbiBnZW5lX2NvcnJlc3AKZ2VuZV9jb3JyZXNwID0gZ2VuZV9jb3JyZXNwICU+JQogIGRwbHlyOjpmaWx0ZXIoIWR1cGxpY2F0ZWQoZ2VuZV9uYW1lKSkgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5lX25hbWUgJWluJSBjb21tb25fZ2VuZXMpICU+JQogIGByb3duYW1lczwtYCguJGdlbmVfbmFtZSkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkVuc2VtYmxfSUQiLCAiZ2VuZV9uYW1lIikpCmdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcFtjb21tb25fZ2VuZXMsIF0KCiMgU3Vic2V0IGdlbmVzIGluIHRoZSBtYXRyaXgKbWF0ID0gbWF0W2NvbW1vbl9nZW5lcywgXQoKIyBHZW5lcyBhcmUgaW4gdGhlIHNhbWUgb3JkZXIKYWxsLmVxdWFsKGdlbmVfY29ycmVzcCRnZW5lX25hbWUsIHJvd25hbWVzKG1hdCkpCmBgYAoKV2UgY3JlYXRlIGEgU2V1cmF0IG9iamVjdCB3aXRoIHRoZSBnZW5lcyBmb3Igd2hpY2ggRW5zZW1ibCBJRCBhcmUgYXZhaWxhYmxlOgoKYGBge3IgY3JlYXRlX3NvYmp9CnNvYmogPSBTZXVyYXQ6OkNyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBtYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gc2FtcGxlX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiKQpzb2JqCmBgYAoKV2UgYWRkIHRoZSBjb3JyZXNwb25kZW5jZSBpbiB0aGUgU2V1cmF0IG9iamVjdCA6CgpgYGB7ciBhZGRfZ2VuZV9jb3JyZXNwfQpzb2JqQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlcyA9IGdlbmVfY29ycmVzcApybShnZW5lX2NvcnJlc3ApCgpoZWFkKHNvYmpAYXNzYXlzJFJOQUBtZXRhLmZlYXR1cmVzKQpgYGAKCldlIGFkZCB0aGUgc2FtZSBjb2x1bW5zIGFzIGluIG1ldGFkYXRhIDoKCmBgYHtyIGFkZF9tZXRhZGF0YX0Kcm93X29pID0gKHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSA9PSBzYW1wbGVfbmFtZSkKCnNvYmokcHJvamVjdF9uYW1lID0gc2FtcGxlX25hbWUKc29iaiRzYW1wbGVfaWRlbnRpZmllciA9IHNhbXBsZV9pbmZvW3Jvd19vaSwgInNhbXBsZV9pZGVudGlmaWVyIl0Kc29iaiRzYW1wbGVfdHlwZSA9IHNhbXBsZV9pbmZvW3Jvd19vaSwgInNhbXBsZV90eXBlIl0Kc29iaiRsb2NhdGlvbiA9IHNhbXBsZV9pbmZvW3Jvd19vaSwgImxvY2F0aW9uIl0Kc29iaiRsYWJvcmF0b3J5ID0gc2FtcGxlX2luZm9bcm93X29pLCAibGFib3JhdG9yeSJdCgpjb2xuYW1lcyhzb2JqQG1ldGEuZGF0YSkKYGBgCgojIEJlZm9yZSBmaWx0ZXJpbmcKCiMjIE5vcm1hbGl6YXRpb24KCmBgYHtyIG5vcm1hbGl6YXRpb259CnNvYmogPSBTZXVyYXQ6Ok5vcm1hbGl6ZURhdGEoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIpCgpzb2JqID0gU2V1cmF0OjpGaW5kVmFyaWFibGVGZWF0dXJlcyhzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZmVhdHVyZXMgPSAzMDAwKQpzb2JqCmBgYAoKIyMgUHJvamVjdGlvbgoKV2UgZ2VuZXJhdGUgYSBVTUFQIHRvIHZpc3VhbGl6ZSBjZWxscyBiZWZvcmUgZmlsdGVyaW5nLgoKYGBge3IgcGNhX2JlZm9yZSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQpzb2JqID0gYXF1YXJpdXM6OmRpbWVuc2lvbnNfcmVkdWN0aW9uKHNvYmogPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4X2RpbXMgPSA1MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpClNldXJhdDo6RWxib3dQbG90KHNvYmosIG5kaW1zID0gNTAsIHJlZHVjdGlvbiA9ICJSTkFfcGNhIikKYGBgCgoKV2UgZ2VuZXJhdGUgYSBVTUFQIHdpdGggMjAgcHJpbmNpcGFsIGNvbXBvbmVudHMgOgoKYGBge3IgdW1hcF9iZWZvcmV9Cm5kaW1zID0gMjAKc29iaiA9IFNldXJhdDo6UnVuVU1BUChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhIiwKICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTpuZGltcywKICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi5uYW1lID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3VtYXAiKSkKCnNvYmoKYGBgCgojIyBDZWxsIHR5cGUKCldlIGFubm90YXRlIGNlbGxzIGZvciBjZWxsIHR5cGUgdXNpbmcgYFNldXJhdDo6QWRkTW9kdWxlU2NvcmVgIGZ1bmN0aW9uLgoKYGBge3IgY2VsbF9hbm5vdF9jdXN0b21fc2hvcnQsIHRpbWVfaXQgPSBUUlVFfQpzb2JqID0gYXF1YXJpdXM6OmNlbGxfYW5ub3RfY3VzdG9tKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3bmFtZSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmtlcnMgPSBjZWxsX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlX25lZ2F0aXZlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGRfc2NvcmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBUUlVFKQoKY29sbmFtZXMoc29iakBtZXRhLmRhdGEpID0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKHN0cmluZyA9IGNvbG5hbWVzKHNvYmpAbWV0YS5kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJfIikKCnNvYmokY2VsbF90eXBlID0gZmFjdG9yKHNvYmokY2VsbF90eXBlLCBsZXZlbHMgPSBuYW1lcyhjZWxsX21hcmtlcnMpKQoKdGFibGUoc29iaiRjZWxsX3R5cGUpCmBgYAoKVG8ganVzdGlmeSBjZWxsIHR5cGUgYW5ub3RhdGlvbiwgd2UgY2FuIG1ha2UgYSBkb3RwbG90IDoKCmBgYHtyIGRvdHBsb3RfY2VsbF90eXBlX3Nob3J0LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDl9Cm1hcmtlcnMgPSBjKCJQVFBSQyIsICJNU1gyIiwgIktSVDE2IiwKICAgICAgICAgICAgdW5pcXVlKHVubGlzdChkb3RwbG90X21hcmtlcnNbbGV2ZWxzKHNvYmokY2VsbF90eXBlKV0pKSkKbWFya2VycyA9IG1hcmtlcnNbbWFya2VycyAlaW4lIHJvd25hbWVzKHNvYmopXQoKYXF1YXJpdXM6OnBsb3RfZG90cGxvdChzb2JqLCBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWFya2VycyA9IG1hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgbmJfaGxpbmUgPSAwKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjo6Y29sb3JfZ2VuZSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpXZSBjYW4gbWFrZSBhIGJhcnBsb3QgdG8gc2VlIHRoZSBjb21wb3NpdGlvbiBvZiBlYWNoIGRhdGFzZXQsIGFuZCB2aXN1YWxpemUgY2VsbCB0eXBlcyBvbiB0aGUgcHJvamVjdGlvbi4KCmBgYHtyIGJhcnBsb3RfY2VsbHR5cGUsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KZGZfcHJvcG9ydGlvbiA9IGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZSh0YWJsZShzb2JqJG9yaWcuaWRlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iaiRjZWxsX3R5cGUpKSkKY29sbmFtZXMoZGZfcHJvcG9ydGlvbikgPSBjKCJvcmlnLmlkZW50IiwgImNlbGxfdHlwZSIsICJmcmVxIikKCnF1YW50aWYgPSB0YWJsZShzb2JqJG9yaWcuaWRlbnQpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygib3JpZy5pZGVudCIsICJuYl9jZWxscyIpKQoKIyBQbG90CnBsb3RfbGlzdCA9IGxpc3QoKQoKcGxvdF9saXN0W1syXV0gPSBhcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gZGZfcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAib3JpZy5pZGVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gImZyZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBnZ3Bsb3QyOjpwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkNlbGwgdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gY29sb3JfbWFya2Vyc1tsZXZlbHMoZGZfcHJvcG9ydGlvbiRjZWxsX3R5cGUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBsZXZlbHMoZGZfcHJvcG9ydGlvbiRjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gb3JpZy5pZGVudCwgeSA9IDEuMDUsIGxhYmVsID0gbmJfY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDApCgpwbG90X2xpc3RbWzFdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJjZWxsX3R5cGUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gc2FtcGxlX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChuY29sKHNvYmopLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkgKyBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbnJvdyA9IDEsIHdpZHRocyA9IGMoNiwgMSkpCmBgYAoKIyMgQ2VsbCBjeWNsZSBwaGFzZQoKV2UgYW5ub3RhdGUgY2VsbHMgZm9yIGNlbGwgY3ljbGUgcGhhc2UgdXNpbmcgYFNldXJhdGAgYW5kIGBjeWNsb25lYC4KCmBgYHtyIGNlbGxfY3ljbGUsIHRpbWVfaXQgPSBUUlVFfQpjY19jb2x1bW5zID0gYXF1YXJpdXM6OmFkZF9jZWxsX2N5Y2xlKHNvYmogPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BlY2llc19yZHggPSAiaHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBjbClAbWV0YS5kYXRhWywgYygiU2V1cmF0LlBoYXNlIiwgIlBoYXNlIildCgpzb2JqJFNldXJhdC5QaGFzZSA9IGNjX2NvbHVtbnMkU2V1cmF0LlBoYXNlCnNvYmokY3ljbG9uZS5QaGFzZSA9IGNjX2NvbHVtbnMkUGhhc2UKCnRhYmxlKHNvYmokU2V1cmF0LlBoYXNlLCBzb2JqJGN5Y2xvbmUuUGhhc2UpCmBgYAoKV2UgdmlzdWFsaXplIGNlbGwgY3ljbGUgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX2NlbGxfY3ljbGUxLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDcsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpwbG90X2xpc3QgPSBsaXN0KCkKCnBsb3RfbGlzdFtbMl1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gIlNldXJhdC5QaGFzZSIpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNlbGwgQ3ljbGUgUGhhc2UiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiU2V1cmF0LlBoYXNlIikgKwogIFNldXJhdDo6Tm9MZWdlbmQoKSArIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpwbG90X2xpc3RbWzFdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJjeWNsb25lLlBoYXNlIikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2VsbCBDeWNsZSBQaGFzZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJjeWNsb25lLlBoYXNlIikgKwogIFNldXJhdDo6Tm9MZWdlbmQoKSArIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMSkKYGBgCgojIFF1YWxpdHkgY29udHJvbAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBsb29rIGF0IHRoZSBudW1iZXIgb2YgZ2VuZXMgZXhwcmVzc2VkIGJ5IGVhY2ggY2VsbCwgdGhlIG51bWJlciBvZiBVTUksIHRoZSBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgZXhwcmVzc2VkLCBhbmQgdGhlIHBlcmNlbnRhZ2Ugb2Ygcmlib3NvbWFsIGdlbmVzIGV4cHJlc3NlZC4KCldlIGNvbXB1dGUgZm91ciBxdWFsaXR5IG1ldHJpY3MgOgoKYGBge3IgcWNfbWV0cmljc30Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoc29iaiwgcGF0dGVybiA9ICJeTVQiLCBjb2wubmFtZSA9ICJwZXJjZW50Lm10IikKc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoc29iaiwgcGF0dGVybiA9ICJeUlBbTHxTXVswLTldKiQiLCBjb2wubmFtZSA9ICJwZXJjZW50LnJiIikKc29iaiRsb2dfbkNvdW50X1JOQSA9IGxvZyhzb2JqJG5Db3VudF9STkEpCgpoZWFkKHNvYmpAbWV0YS5kYXRhKQpgYGAKCldlIGdldCB0aGUgY2VsbCBiYXJjb2RlcyBmb3IgdGhlIGZhaWxpbmcgY2VsbHMgOgoKYGBge3IgZmFpbGVkfQpmYWlsX3BlcmNlbnQubXQgPSBzb2JqQG1ldGEuZGF0YSAlPiUgZHBseXI6OmZpbHRlcihwZXJjZW50Lm10ID4gY3V0X3BlcmNlbnQubXQpICU+JSByb3duYW1lcygpCmZhaWxfcGVyY2VudC5yYiA9IHNvYmpAbWV0YS5kYXRhICU+JSBkcGx5cjo6ZmlsdGVyKHBlcmNlbnQucmIgPiBjdXRfcGVyY2VudC5yYikgJT4lIHJvd25hbWVzKCkKZmFpbF9sb2dfbkNvdW50X1JOQSA9IHNvYmpAbWV0YS5kYXRhICU+JSBkcGx5cjo6ZmlsdGVyKGxvZ19uQ291bnRfUk5BIDwgY3V0X2xvZ19uQ291bnRfUk5BKSAlPiUgcm93bmFtZXMoKQpmYWlsX25GZWF0dXJlX1JOQSA9IHNvYmpAbWV0YS5kYXRhICU+JSBkcGx5cjo6ZmlsdGVyKG5GZWF0dXJlX1JOQSA8IGN1dF9uRmVhdHVyZV9STkEpICU+JSByb3duYW1lcygpCmBgYAoKIyMgUXVhbGl0eSBjb250cm9sIHJlcHJlc2VudGF0aW9uCgpXZSBjYW4gdmlzdWFsaXplIHRoZSA0IGNlbGxzIHF1YWxpdHkgd2l0aCBhIFZlbm4gZGlhZ3JhbSA6IAoKYGBge3IgcWNfdmVubiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQpuX2ZpbHRlcmVkID0gYyhmYWlsX3BlcmNlbnQubXQsIGZhaWxfcGVyY2VudC5yYiwgZmFpbF9sb2dfbkNvdW50X1JOQSwgZmFpbF9uRmVhdHVyZV9STkEpICU+JQogIHVuaXF1ZSgpICU+JSBsZW5ndGgoKQpwZXJjZW50X2ZpbHRlcmVkID0gcm91bmQoMTAwKihuX2ZpbHRlcmVkL25jb2woc29iaikpLCAyKQoKZ2d2ZW5uOjpnZ3Zlbm4obGlzdChwZXJjZW50Lm10ID0gZmFpbF9wZXJjZW50Lm10LAogICAgICAgICAgICAgICAgICAgIHBlcmNlbnQucmIgPSBmYWlsX3BlcmNlbnQucmIsCiAgICAgICAgICAgICAgICAgICAgbG9nX25Db3VudF9STkEgPSBmYWlsX2xvZ19uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgIG5GZWF0dXJlX1JOQSA9IGZhaWxfbkZlYXR1cmVfUk5BKSwgCiAgICAgICAgICAgICAgIGZpbGxfY29sb3IgPSBjKCIjMDA3M0MyRkYiLCAiI0VGQzAwMEZGIiwgIm9yYW5nZSIsICJwaW5rIiksCiAgICAgICAgICAgICAgIHN0cm9rZV9zaXplID0gMC41LCBzZXRfbmFtZV9zaXplID0gNCkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiRmlsdGVyZWQgb3V0IGNlbGxzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5fZmlsdGVyZWQsICIgY2VsbHMgKCIsIHBlcmNlbnRfZmlsdGVyZWQsICIgJSBvZiBhbGwgY2VsbHMpIikpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgoKIyMjIE51bWJlciBvZiBVTUkKClRvIHZpc3VhbGl6ZSB0aGUgdGhyZXNob2xkIGZvciBudW1iZXIgb2YgVU1JLCB3ZSBjYW4gbWFrZSBhIGhpc3RvZ3JhbSA6CgpgYGB7ciBxY191bWlfaGlzdCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KYXF1YXJpdXM6OnBsb3RfcWNfZGVuc2l0eShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAyMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAib3JpZy5pZGVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBzZXROYW1lcyhzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpYW50KSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4X3RocmVzaCA9IGN1dF9sb2dfbkNvdW50X1JOQSkKYGBgCgpgYGB7ciB2bG5fdW1pX2NlbGxfdHlwZSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZmVhdHVyZXMgPSAibG9nX25Db3VudF9STkEiLCBwdC5zaXplID0gMC4wMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX21hcmtlcnMsIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X2xvZ19uQ291bnRfUk5BLCBjb2wgPSAicmVkIikgKwogIGdncGxvdDI6OmxhYnMoeCA9ICIiKQpgYGAKCmBgYHtyIHFjX3VtaV9wcm9qLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gN30Kc29iaiRmYWlsID0gaWZlbHNlKGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9sb2dfbkNvdW50X1JOQSwKICAgICAgICAgICAgICAgICAgIHllcyA9IGFzLmNoYXJhY3Rlcihzb2JqJGNlbGxfdHlwZSksIG5vID0gTkEpCnNvYmokZmFpbCA9IGZhY3Rvcihzb2JqJGZhaWwsIGxldmVscyA9IGMobGV2ZWxzKHNvYmokY2VsbF90eXBlKSwgTkEpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImZhaWwiLCBuYS52YWx1ZSA9ICJncmF5ODAiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxfbG9nX25Db3VudF9STkEpLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCiMjIyBOdW1iZXIgb2YgZmVhdHVyZXMKClRvIHZpc3VhbGl6ZSB0aGUgdGhyZXNob2xkIGZvciBudW1iZXIgb2YgZmVhdHVyZXMsIHdlIGNhbiBtYWtlIGEgaGlzdG9ncmFtIDoKCmBgYHtyIHFjX2ZlYXR1cmVzX2hpc3QsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9CmFxdWFyaXVzOjpwbG90X3FjX2RlbnNpdHkoZGYgPSBzb2JqQG1ldGEuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IHNldE5hbWVzKHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmlhbnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X25GZWF0dXJlX1JOQSkKYGBgCgoKYGBge3IgdmxuX2ZlYXR1cmVzX2NlbGxfdHlwZSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZmVhdHVyZXMgPSAibkZlYXR1cmVfUk5BIiwgcHQuc2l6ZSA9IDAuMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6Omdlb21faGxpbmUoeWludGVyY2VwdCA9IGN1dF9uRmVhdHVyZV9STkEsIGNvbCA9ICJyZWQiKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIiIpCmBgYAoKYGBge3IgcWNfZmVhdHVyZXNfcHJvaiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CnNvYmokZmFpbCA9IGlmZWxzZShjb2xuYW1lcyhzb2JqKSAlaW4lIGZhaWxfbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgeWVzID0gYXMuY2hhcmFjdGVyKHNvYmokY2VsbF90eXBlKSwgbm8gPSBOQSkKc29iaiRmYWlsID0gZmFjdG9yKHNvYmokZmFpbCwgbGV2ZWxzID0gYyhsZXZlbHMoc29iaiRjZWxsX3R5cGUpLCBOQSkpCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiZmFpbCIsIG5hLnZhbHVlID0gImdyYXk4MCIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJuRmVhdHVyZV9STkEiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxfbkZlYXR1cmVfUk5BKSwgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgojIyMgTWl0b2Nob25kcmlhbCBnZW5lcyBleHByZXNzaW9uCgpUbyBpZGVudGlmeSBhIHRocmVzaG9sZCBmb3IgbWl0b2Nob25kcmlhbCBnZW5lIGV4cHJlc3Npb24sIHdlIGNhbiBtYWtlIGEgaGlzdG9ncmFtIDoKCmBgYHtyIHFjX21pdG9faGlzdCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KYXF1YXJpdXM6OnBsb3RfcWNfZGVuc2l0eShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAicGVyY2VudC5tdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IHNldE5hbWVzKHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmlhbnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X3BlcmNlbnQubXQpCmBgYAoKYGBge3IgdmxuX3BlcmNlbnRtdF9jZWxsX3R5cGUsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KU2V1cmF0OjpWbG5QbG90KHNvYmosIGZlYXR1cmVzID0gInBlcmNlbnQubXQiLCBwdC5zaXplID0gMC4wMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX21hcmtlcnMsIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3BlcmNlbnQubXQsIGNvbCA9ICJyZWQiKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIiIpCmBgYAoKYGBge3IgcWNfbWl0b19wcm9qLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gN30Kc29iaiRmYWlsID0gaWZlbHNlKGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9wZXJjZW50Lm10LAogICAgICAgICAgICAgICAgICAgeWVzID0gYXMuY2hhcmFjdGVyKHNvYmokY2VsbF90eXBlKSwgbm8gPSBOQSkKc29iaiRmYWlsID0gZmFjdG9yKHNvYmokZmFpbCwgbGV2ZWxzID0gYyhsZXZlbHMoc29iaiRjZWxsX3R5cGUpLCBOQSkpCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiZmFpbCIsIG5hLnZhbHVlID0gImdyYXk4MCIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJwZXJjZW50Lm10IiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKGxlbmd0aChmYWlsX3BlcmNlbnQubXQpLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCiMjIyBSaWJvc29tYWwgZ2VuZXMgZXhwcmVzc2lvbgoKVG8gaWRlbnRpZnkgYSB0aHJlc2hvbGQgZm9yIHJpYm9zb21hbCBnZW5lIGV4cHJlc3Npb24sIHdlIGNhbiBtYWtlIGEgaGlzdG9ncmFtIDoKCmBgYHtyIHFjX3JpYm9faGlzdCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KYXF1YXJpdXM6OnBsb3RfcWNfZGVuc2l0eShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAicGVyY2VudC5yYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IHNldE5hbWVzKHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmlhbnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X3BlcmNlbnQucmIpCmBgYAoKCmBgYHtyIHZsbl9wZXJjZW50cmJfY2VsbF90eXBlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9ClNldXJhdDo6VmxuUGxvdChzb2JqLCBmZWF0dXJlcyA9ICJwZXJjZW50LnJiIiwgcHQuc2l6ZSA9IDAuMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6Omdlb21faGxpbmUoeWludGVyY2VwdCA9IGN1dF9wZXJjZW50LnJiLCBjb2wgPSAicmVkIikgKwogIGdncGxvdDI6OmxhYnMoeCA9ICIiKQpgYGAKCmBgYHtyIHFjX3JpYm9fcHJvaiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CnNvYmokZmFpbCA9IGlmZWxzZShjb2xuYW1lcyhzb2JqKSAlaW4lIGZhaWxfcGVyY2VudC5yYiwKICAgICAgICAgICAgICAgICAgIHllcyA9IGFzLmNoYXJhY3Rlcihzb2JqJGNlbGxfdHlwZSksIG5vID0gTkEpCnNvYmokZmFpbCA9IGZhY3Rvcihzb2JqJGZhaWwsIGxldmVscyA9IGMobGV2ZWxzKHNvYmokY2VsbF90eXBlKSwgTkEpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImZhaWwiLCBuYS52YWx1ZSA9ICJncmF5ODAiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAicGVyY2VudC5yYiIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChsZW5ndGgoZmFpbF9wZXJjZW50LnJiKSwgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgojIyMgRkFDUy1saWtlIGZpZ3VyZQoKV2Ugd291bGQgbGlrZSB0byBzZWUgaWYgdGhlIG51bWJlciBvZiBmZWF0dXJlIGV4cHJlc3NlZCBieSBjZWxsLCBhbmQgdGhlIG51bWJlciBvZiBVTUkgaXMgY29ycmVsYXRlZCB3aXRoIHRoZSBjZWxsIHR5cGUsIHRoZSBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgYW5kIHJpYm9zb21hbCBnZW5lIGV4cHJlc3NlZC4gV2UgYnVpbGQgdGhlIGBsb2dfbkNvdW50X1JOQWAgYnkgYG5GZWF0dXJlX1JOQWAgZmlndXJlLCB3aGVyZSBjZWxscyAoZG90cykgYXJlIGNvbG9yZWQgYnkgdGhlc2UgZGlmZmVyZW50IG1ldHJpY3MuCgpUaGlzIGlzIHRoZSBmaWd1cmUsIGNvbG9yZWQgYnkgY2VsbCB0eXBlIDoKCmBgYHtyIHFjX3BhdGNod29ya19jZWxsX3R5cGUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CmFxdWFyaXVzOjpwbG90X3FjX2ZhY3NsaWtlKGRmID0gc29iakBtZXRhLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAibkZlYXR1cmVfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICJsb2dfbkNvdW50X1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF9ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfY29sb3JzID0gdW5uYW1lKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4X3RocmVzaCA9IGN1dF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfdGhyZXNoID0gY3V0X2xvZ19uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwKQpgYGAKClRoaXMgaXMgdGhlIGZpZ3VyZSwgY29sb3JlZCBieSB0aGUgcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIGV4cHJlc3NlZCBpbiBjZWxsIDoKCmBgYHtyIHFjX3BhdGNod29ya19taXRvLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQphcXVhcml1czo6cGxvdF9xY19mYWNzbGlrZShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfYnkgPSAicGVyY2VudC5tdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeV90aHJlc2ggPSBjdXRfbG9nX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAyMDApCmBgYAoKVGhpcyBpcyB0aGUgZmlndXJlLCBjb2xvcmVkIGJ5IHRoZSBwZXJjZW50YWdlIG9mIHJpYm9zb21hbCBnZW5lcyBleHByZXNzZWQgaW4gY2VsbCA6CgpgYGB7ciBxY19wYXRjaHdvcmtfcmlibywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KYXF1YXJpdXM6OnBsb3RfcWNfZmFjc2xpa2UoZGYgPSBzb2JqQG1ldGEuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJuRmVhdHVyZV9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gImxvZ19uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX2J5ID0gInBlcmNlbnQucmIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4X3RocmVzaCA9IGN1dF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfdGhyZXNoID0gY3V0X2xvZ19uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwKQpgYGAKCgojIyMgVmlzdWFsaXphdGlvbiBhcyBwaWVjaGFydAoKRG8gZmlsdGVyZWQgY2VsbHMgYmVsb25nIHRvIGEgcGFydGljdWxhciBjZWxsIHR5cGUgPwoKYGBge3IgcWNfcGllY2hhcnRfY2VsbF90eXBlLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA5fQpzb2JqJGFsbF9jZWxscyA9IFRSVUUKCnBsb3RfbGlzdCA9IGxpc3QoKQoKIyMgQWxsIGNlbGxzCmRmID0gc29iakBtZXRhLmRhdGEKaWYgKG5yb3coZGYpID09IDApIHsKICBwbG90X2xpc3RbWzFdXSA9IGdncGxvdCgpCn0gZWxzZSB7CiAgcGxvdF9saXN0W1sxXV0gPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiA9IGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naWNhbF92YXIgPSAiYWxsX2NlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlfbGVnZW5kID0gVFJVRSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJBbGwgY2VsbHMiLAogICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKG5yb3coZGYpLCAiY2VsbHMiKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKfQoKIyMgRG91YmxldCBjZWxsczogbm90IGRvbmUKcGxvdF9saXN0W1syXV0gPSBnZ3Bsb3QoKQoKIyMgcGVyY2VudC5tdApkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIocGVyY2VudC5tdCA+IGN1dF9wZXJjZW50Lm10KQppZiAobnJvdyhkZikgPT0gMCkgewogIHBsb3RfbGlzdFtbM11dID0gZ2dwbG90KCkKfSBlbHNlIHsKICBwbG90X2xpc3RbWzNdXSA9IGFxdWFyaXVzOjpwbG90X3BpZWNoYXJ0KGRmID0gZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dpY2FsX3ZhciA9ICJhbGxfY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmdfdmFyID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheV9sZWdlbmQgPSBUUlVFKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoInBlcmNlbnQubXQgPiIsIGN1dF9wZXJjZW50Lm10KSwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZShsZW5ndGgoZmFpbF9wZXJjZW50Lm10KSwgImNlbGxzIikpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCn0KCiMjIHBlcmNlbnQucmIKZGYgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHBlcmNlbnQucmIgPiBjdXRfcGVyY2VudC5yYikKaWYgKG5yb3coZGYpID09IDApIHsKICBwbG90X2xpc3RbWzRdXSA9IGdncGxvdCgpCn0gZWxzZSB7CiAgcGxvdF9saXN0W1s0XV0gPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiA9IGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naWNhbF92YXIgPSAiYWxsX2NlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlfbGVnZW5kID0gVFJVRSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhc3RlKCJwZXJjZW50LnJiID4iLCBjdXRfcGVyY2VudC5yYiksCiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUobGVuZ3RoKGZhaWxfcGVyY2VudC5yYiksICJjZWxscyIpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQp9CgojIyBsb2dfbkNvdW50X1JOQQpkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIobG9nX25Db3VudF9STkEgPCBjdXRfbG9nX25Db3VudF9STkEpCmlmIChucm93KGRmKSA9PSAwKSB7CiAgcGxvdF9saXN0W1s1XV0gPSBnZ3Bsb3QoKQp9IGVsc2UgewogIHBsb3RfbGlzdFtbNV1dID0gYXF1YXJpdXM6OnBsb3RfcGllY2hhcnQoZGYgPSBkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ljYWxfdmFyID0gImFsbF9jZWxscyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5X2xlZ2VuZCA9IFRSVUUpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXN0ZSgibG9nX25Db3VudF9STkEgPCIsIHJvdW5kKGN1dF9sb2dfbkNvdW50X1JOQSwgMikpLAogICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKGxlbmd0aChmYWlsX2xvZ19uQ291bnRfUk5BKSwgImNlbGxzIikpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCn0KCiMjIG5GZWF0dXJlX1JOQQpkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIobkZlYXR1cmVfUk5BIDwgY3V0X25GZWF0dXJlX1JOQSkKaWYgKG5yb3coZGYpID09IDApIHsKICBwbG90X2xpc3RbWzZdXSA9IGdncGxvdCgpCn0gZWxzZSB7CiAgcGxvdF9saXN0W1s2XV0gPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiA9IGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naWNhbF92YXIgPSAiYWxsX2NlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlfbGVnZW5kID0gVFJVRSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhc3RlKCJuRmVhdHVyZV9STkEgPCIsIHJvdW5kKGN1dF9uRmVhdHVyZV9STkEsIDIpKSwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZShsZW5ndGgoZmFpbF9uRmVhdHVyZV9STkEpLCAiY2VsbHMiKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKfQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJgogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKYGBge3IgY2xlYW5fcWNfNCwgZWNobyA9IEZBTFNFfQpzb2JqJGFsbF9jZWxscyA9IE5VTEwKCnJtKHBsb3RfbGlzdCwgZGYpCmBgYAoKIyMgU2F2ZQoKV2UgY291bGQgc2F2ZSB0aGlzIG9iamVjdCBiZWZvcmUgZmlsdGVyaW5nIChyZW1vdmUgYGV2YWwgPSBGQUxTRWApIDoKCmBgYHtyIHNhdmVfc29ial91bmZpbHRlcmVkX2Fubm90YXRlZCwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKHNvYmosIHBhc3RlMChvdXRfZGlyLCAiL2RhdGFzZXRzLyIsIHNhbXBsZV9uYW1lLCAiX3NvYmpfdW5maWx0ZXJlZC5yZHMiKSkKYGBgCgoKIyBGaWx0ZXJpbmcKCldlIHJlbW92ZSA6CgoqIGNlbGxzIHdpdGggYSBudW1iZXIgb2YgVU1JIGxvd2VyIHRoYW4gYHIgY3V0X2xvZ19uQ291bnRfUk5BYAoqIGNlbGxzIGV4cHJlc3NpbmcgYSBudW1iZXIgb2YgZ2VuZXMgbG93ZXIgdGhhbiBgciBjdXRfbkZlYXR1cmVfUk5BYAoqIGNlbGxzIGhhdmluZyBtb3JlIHRoYW4gYHIgY3V0X3BlcmNlbnQubXRgIFwlIG9mIFVNSSByZWxhdGVkIHRvIG1pdG9jaG9uZHJpYWwgZ2VuZXMKKiBjZWxscyBoYXZpbmcgbW9yZSB0aGFuIGByIGN1dF9wZXJjZW50LnJiYCBcJSBvZiBVTUkgcmVsYXRlZCB0byByaWJvc29tYWwgZ2VuZXMKCioqTm90ZSoqOiBXZSBkbyBub3QgZmlsdGVyIGNlbGxzIGRldGVjdGVkIGFzIGRvdWJsZXRzLiBJbmRlZWQsIGZldyBnZW5lcyBhbmQgdHJhbnNjcmlwdHMgYXJlIGRldGVjdGVkIHBlciBjZWxsLCBhbmQgdGhlIGJlc3QgY2VsbHMgYXJlIHRoZXJlZm9yZSBhbm5vdGF0ZWQgYXMgZG91YmxldHMuCgpgYGB7ciBmaWx0ZXJfY2VsbHN9CnNvYmogPSBzdWJzZXQoc29iaiwgaW52ZXJ0ID0gVFJVRSwKICAgICAgICAgICAgICBjZWxscyA9IHVuaXF1ZShjKGZhaWxfbG9nX25Db3VudF9STkEsIGZhaWxfbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpbF9wZXJjZW50Lm10LCBmYWlsX3BlcmNlbnQucmIpKSkKc29iagpgYGAKCmBgYHtyIGNsZWFuX2ZpbHRlciwgZWNobyA9IEZBTFNFfQpybShmYWlsX3BlcmNlbnQubXQsIGZhaWxfcGVyY2VudC5yYiwgZmFpbF9sb2dfbkNvdW50X1JOQSwgZmFpbF9uRmVhdHVyZV9STkEsCiAgIGN1dF9wZXJjZW50Lm10LCBjdXRfcGVyY2VudC5yYiwgY3V0X2xvZ19uQ291bnRfUk5BLCBjdXRfbkZlYXR1cmVfUk5BKQpgYGAKCgojIFBvc3QtZmlsdGVyaW5nIHByb2Nlc3NpbmcKCiMjIE5vcm1hbGl6YXRpb24KCldlIG5vcm1hbGl6ZSB0aGUgY291bnQgbWF0cml4IGZvciByZW1haW5pbmcgY2VsbHMgOgoKYGBge3Igbm9ybWFsaXphdGlvbl8zfQpzb2JqID0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiKQoKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gMzAwMCkKc29iagpgYGAKCiMjIFByb2plY3Rpb24KCldlIHBlcmZvcm0gYSBQQ0EgOgoKYGBge3IgcGNhLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CnNvYmogPSBhcXVhcml1czo6ZGltZW5zaW9uc19yZWR1Y3Rpb24oc29iaiA9IHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAicGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZGltcyA9IDUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKU2V1cmF0OjpFbGJvd1Bsb3Qoc29iaiwgbmRpbXMgPSA1MCwgcmVkdWN0aW9uID0gIlJOQV9wY2EiKQpgYGAKCgpXZSBnZW5lcmF0ZSBhIFVNQVAgd2l0aCAyMCBwcmluY2lwYWwgY29tcG9uZW50cyA6CgpgYGB7ciB1bWFwfQpuZGltcyA9IDIwCnNvYmogPSBTZXVyYXQ6OlJ1blVNQVAoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiUk5BX3BjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6bmRpbXMsCiAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxMzM3TCwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9IHBhc3RlMCgiUk5BX3BjYV8iLCBuZGltcywgIl91bWFwIikpCmBgYAoKCiMjIEFubm90YXRpb24KCldlIGFubm90YXRlIGNlbGxzIGZvciBjZWxsIHR5cGUsIHdpdGggdGhlIG5ldyBub3JtYWxpemVkIGV4cHJlc3Npb24gbWF0cml4IDoKCmBgYHtyIGNlbGxfdHlwZV8yLCB0aW1lX2l0ID0gVFJVRX0Kc2NvcmVfY29sdW1ucyA9IGdyZXAoeCA9IGNvbG5hbWVzKHNvYmpAbWV0YS5kYXRhKSwgcGF0dGVybiA9ICJec2NvcmUiLCB2YWx1ZSA9IFRSVUUpCnNvYmpAbWV0YS5kYXRhWywgc2NvcmVfY29sdW1uc10gPSBOVUxMCnNvYmokY2VsbF90eXBlID0gTlVMTAoKc29iaiA9IGFxdWFyaXVzOjpjZWxsX2Fubm90X2N1c3RvbShzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld25hbWUgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gY2VsbF9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZV9uZWdhdGl2ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX3Njb3JlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkKCnNvYmokY2VsbF90eXBlID0gZmFjdG9yKHNvYmokY2VsbF90eXBlLCBsZXZlbHMgPSBuYW1lcyhjZWxsX21hcmtlcnMpKQoKY29sbmFtZXMoc29iakBtZXRhLmRhdGEpID0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKHN0cmluZyA9IGNvbG5hbWVzKHNvYmpAbWV0YS5kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJfIikKCnRhYmxlKHNvYmokY2VsbF90eXBlKQpgYGAKCgpUbyBqdXN0aWZ5IGNlbGwgdHlwZSBhbm5vdGF0aW9uLCB3ZSBjYW4gbWFrZSBhIGRvdHBsb3QgOgoKYGBge3IgZG90cGxvdF9jZWxsX3R5cGVfc2hvcnQyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDl9Cm1hcmtlcnMgPSBjKCJQVFBSQyIsIHVuaXF1ZSh1bmxpc3QoZG90cGxvdF9tYXJrZXJzW2xldmVscyhzb2JqJGNlbGxfdHlwZSldKSkpCm1hcmtlcnMgPSBtYXJrZXJzW21hcmtlcnMgJWluJSByb3duYW1lcyhzb2JqKV0KCmFxdWFyaXVzOjpwbG90X2RvdHBsb3Qoc29iaiwgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIG1hcmtlcnMgPSBtYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgIG5iX2hsaW5lID0gMCkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6OmNvbG9yX2dlbmUpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKV2UgY2FuIG1ha2UgYSBiYXJwbG90IHRvIHNlZSB0aGUgY29tcG9zaXRpb24gb2YgZWFjaCBkYXRhc2V0LCBhbmQgdmlzdWFsaXplIGNlbGwgdHlwZXMgb24gdGhlIHByb2plY3Rpb24uCgpgYGB7ciBiYXJwbG90X2NlbGx0eXBlMiwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpkZl9wcm9wb3J0aW9uID0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNvYmokb3JpZy5pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNlbGxfdHlwZSkpKQpjb2xuYW1lcyhkZl9wcm9wb3J0aW9uKSA9IGMoIm9yaWcuaWRlbnQiLCAiY2VsbF90eXBlIiwgImZyZXEiKQoKcXVhbnRpZiA9IHRhYmxlKHNvYmokb3JpZy5pZGVudCkgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJvcmlnLmlkZW50IiwgIm5iX2NlbGxzIikpCgojIFBsb3QKcGxvdF9saXN0ID0gbGlzdCgpCgpwbG90X2xpc3RbWzJdXSA9IGFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSBkZl9wcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAiZnJlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IGdncGxvdDI6OnBvc2l0aW9uX2ZpbGwoKSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQ2VsbCB0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzW2xldmVscyhkZl9wcm9wb3J0aW9uJGNlbGxfdHlwZSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhkZl9wcm9wb3J0aW9uJGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBvcmlnLmlkZW50LCB5ID0gMS4wNSwgbGFiZWwgPSBuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCkKCnBsb3RfbGlzdFtbMV1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhXzIwX3VtYXAiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gc2FtcGxlX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChuY29sKHNvYmopLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkgKyBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbnJvdyA9IDEsIHdpZHRocyA9IGMoNiwgMSkpCmBgYAoKCiMjIyBDZWxsIGN5Y2xlCgpXZSBhbm5vdGF0ZSBjZWxscyBmb3IgY2VsbCBjeWNsZSBwaGFzZSA6CgpgYGB7ciBjZWxsX2N5Y2xlMiwgdGltZV9pdCA9IFRSVUV9CmNjX2NvbHVtbnMgPSBhcXVhcml1czo6YWRkX2NlbGxfY3ljbGUoc29iaiA9IHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzX3JkeCA9ICJocyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQlBQQVJBTSA9IGNsKUBtZXRhLmRhdGFbLCBjKCJTZXVyYXQuUGhhc2UiLCAiUGhhc2UiKV0KCnNvYmokU2V1cmF0LlBoYXNlID0gY2NfY29sdW1ucyRTZXVyYXQuUGhhc2UKc29iaiRjeWNsb25lLlBoYXNlID0gY2NfY29sdW1ucyRQaGFzZQoKdGFibGUoc29iaiRTZXVyYXQuUGhhc2UsIHNvYmokY3ljbG9uZS5QaGFzZSkKYGBgCgoKV2UgdmlzdWFsaXplIGNlbGwgY3ljbGUgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX2NlbGxfY3ljbGUyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDcsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpwbG90X2xpc3QgPSBsaXN0KCkKCnBsb3RfbGlzdFtbMl1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gIlNldXJhdC5QaGFzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhXzIwX3VtYXAiKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJDZWxsIEN5Y2xlIFBoYXNlIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIlNldXJhdC5QaGFzZSIpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkgKyBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKcGxvdF9saXN0W1sxXV0gPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY3ljbG9uZS5QaGFzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhXzIwX3VtYXAiKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJDZWxsIEN5Y2xlIFBoYXNlIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gImN5Y2xvbmUuUGhhc2UiKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpICsgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5yb3cgPSAxKQpgYGAKCgojIyBDbHVzdGVyaW5nCgpXZSBtYWtlIGEgaGlnaGx5IHJlc29sdXRpdmUgY2x1c3RlcmluZyA6CgpgYGB7ciBjbHVzdGVyaW5nfQpzb2JqID0gU2V1cmF0OjpGaW5kTmVpZ2hib3JzKHNvYmosIHJlZHVjdGlvbiA9ICJSTkFfcGNhIiwgZGltcyA9IGMoMTpuZGltcykpCnNvYmogPSBTZXVyYXQ6OkZpbmRDbHVzdGVycyhzb2JqLCByZXNvbHV0aW9uID0gMikKCnRhYmxlKHNvYmokc2V1cmF0X2NsdXN0ZXJzKQpgYGAKCgojIFZpc3VhbGl6YXRpb24KCiMjIENlbGwgdHlwZQoKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgY2VsbCB0eXBlIDoKCmBgYHtyIHNlZV9jZWxsX3R5cGUsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA2LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJVTUFQIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMgQ2VsbCBjeWNsZQoKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgY2VsbCBjeWNsZSwgZnJvbSBTZXVyYXQgOgoKYGBge3Igc2VlX2NjX1NldXJhdCwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiU2V1cmF0LlBoYXNlIiwKICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHBhc3RlMCgiUk5BX3BjYV8iLCBuZGltcywgIl91bWFwIikpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZSgiVU1BUCIpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBjZWxsIGN5Y2xlLCBmcm9tIGN5Y2xvbmUgOgoKYGBge3Igc2VlX2NjX2N5Y2xvbmUsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA2LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImN5Y2xvbmUuUGhhc2UiLAogICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3VtYXAiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJVTUFQIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKCiMjIENsdXN0ZXJzCgpXZSB2aXN1YWxpemUgdGhlIGNsdXN0ZXJpbmcgOgoKYGBge3Igc2VlX2NsdXN0ZXJzLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9ClNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBsYWJlbCA9IFRSVUUsCiAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUoIlVNQVAiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgoKIyMgR2VuZSBleHByZXNzaW9uCgpXZSB2aXN1YWxpemUgYWxsIGNlbGwgdHlwZXMgbWFya2VycyBvbiB0aGUgVU1BUCA6CgpgYGB7ciBwbG90X2dlbmVzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDIwfQptYXJrZXJzID0gZG90cGxvdF9tYXJrZXJzICU+JSB1bmxpc3QoKSAlPiUgdW5uYW1lKCkKbWFya2VycyA9IG1hcmtlcnNbbWFya2VycyAlaW4lIHJvd25hbWVzKHNvYmopXQoKcGxvdF9saXN0ID0gbGFwcGx5KG1hcmtlcnMsCiAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogICAgICAgICAgICAgICAgICAgICBwID0gU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IG9uZV9nZW5lKSArCiAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogICAgICAgICAgICAgICAgICAgICAgIFNldXJhdDo6Tm9BeGVzKCkKICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKHApCiAgICAgICAgICAgICAgICAgICB9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDQpCmBgYAoKCiMgU2F2ZQoKV2Ugc2F2ZSB0aGUgYW5ub3RhdGVkIGFuZCBmaWx0ZXJlZCBTZXVyYXQgb2JqZWN0IDoKCmBgYHtyIHNhdmVfc29ial9maWx0ZXJlZF9hbm5vdGF0ZWR9CnNhdmVSRFMoc29iaiwgZmlsZSA9IHBhc3RlMChvdXRfZGlyLCAiL2RhdGFzZXRzLyIsIHNhbXBsZV9uYW1lLCAiX3NvYmpfZmlsdGVyZWQucmRzIikpCmBgYAoKCiMgUiBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFLCBmb2xkX291dHB1dCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCg==